package com.example.xViewChat.NettyClient;

import android.content.Context;
import android.content.Intent;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import com.example.xViewChat.Bean.IntentBean;
import com.example.xViewChat.NettyClient.Exception.NetWorkDisableException;
import com.example.xViewChat.NettyClient.Exception.SessionDisableException;
import com.example.xViewChat.NettyClient.Exception.WriteToClosedSessionException;
import com.example.xViewChat.NettyClient.Handler.*;
import io.ganguo.chat.core.codec.PacketDecoder;
import io.ganguo.chat.core.codec.PacketEncoder;
import io.ganguo.chat.core.connetion.IMConnection;
import io.ganguo.chat.core.handler.IMHandler;
import io.ganguo.chat.core.handler.IMHandlerManager;
import io.ganguo.chat.core.protocol.Commands;
import io.ganguo.chat.core.protocol.Handlers;
import io.ganguo.chat.core.transport.Header;
import io.ganguo.chat.core.transport.IMRequest;
import io.ganguo.chat.core.transport.IMResponse;

import io.netty.bootstrap.Bootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.net.InetSocketAddress;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

/**
 * Created by hasee on 2016-03-31.
 */
@ChannelHandler.Sharable
public class ConnectorManager extends SimpleChannelInboundHandler<IMRequest> {
    private final ExecutorService executor;
    private final Bootstrap bootstrap;
    private final NioEventLoopGroup loopGroup;
    Context context;
    static ConnectorManager manager;
    static Channel channel;
    private IMConnection mConnection = null;

    // ��Ϣ�㲥action
    public static final String ACTION_MESSAGE_RECEIVED = "com.MESSAGE_RECEIVED";

    // ����sendbodyʧ�ܹ㲥
    public static final String ACTION_SENT_FAILED = "com.SENT_FAILED";

    // ����sendbody�ɹ��㲥
    public static final String ACTION_SENT_SUCCESS = "com.SENT_SUCCESS";
    // ��������رչ㲥
    public static final String ACTION_CONNECTION_CLOSED = "com.CONNECTION_CLOSED";
    // ����ʧ�ܹ㲥
    public static final String ACTION_CONNECTION_FAILED = "com.CONNECTION_FAILED";
    // ���ӳɹ��㲥
    public static final String ACTION_CONNECTION_SUCCESS = "com.CONNECTION_SUCCESS";
    // ����sendbody�ɹ�����replaybody��Ӧ�㲥
    public static final String ACTION_REPLY_RECEIVED = "com.REPLY_RECEIVED";
    // ����仯�㲥
    public static final String ACTION_NETWORK_CHANGED = "com.CONNECTIVITY_CHANGE";

    // δ֪�쳣
    public static final String ACTION_UNCAUGHT_EXCEPTION = "com.UNCAUGHT_EXCEPTION";

    // CIM����״̬
    public static final String ACTION_CONNECTION_STATUS = "com.CONNECTION_STATUS";
    private Logger logger = LoggerFactory.getLogger(ConnectorManager.class);

    public ConnectorManager(Context context) {
        this.context = context;
        executor = Executors.newFixedThreadPool(3);
        bootstrap = new Bootstrap();
        loopGroup = new NioEventLoopGroup();
        bootstrap.group(loopGroup);
        bootstrap.channel(NioSocketChannel.class);
        bootstrap.handler(new ChannelInitializer<SocketChannel>() {
            @Override
            public void initChannel(SocketChannel ch) throws Exception {
                ChannelPipeline pipeline = ch.pipeline();
                pipeline.addLast("decoder", new PacketDecoder(8192, 0, 4));
                pipeline.addLast("encoder", new PacketEncoder());
                pipeline.addLast(ConnectorManager.this);
            }
        });
        bootstrap.option(ChannelOption.TCP_NODELAY, true);
        initIMHandler();
    }

    private void initIMHandler() {
        IMHandlerManager.getInstance().register(UserHandler.class);
        IMHandlerManager.getInstance().register(MessageHandler.class);
        IMHandlerManager.getInstance().register(OperationHandler.class);
        IMHandlerManager.getInstance().register(HeartbeatHandler.class);
    }

    @Override
    public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
        logger.info("handlerAdded");

        if (mConnection != null) {
            mConnection.close();
            mConnection = null;
        }
        mConnection = new IMConnection(0L, ctx);
    }

    @Override
    protected void channelRead0(ChannelHandlerContext channelHandlerContext, IMRequest request) throws Exception {
        logger.info("messageReceived");


        if (request.getHeader().getHandlerId() == Handlers.HEARTBEAT) {
            IMResponse resp = new IMResponse();
            Header header = new Header();
            header.setHandlerId(Handlers.HEARTBEAT);
            header.setCommandId(Commands.SERVER_HEARTBEAT);
            resp.setHeader(header);
            channelHandlerContext.writeAndFlush(resp);
            return;
        }
        /*Intent intent = new Intent();
        intent.setAction(ACTION_MESSAGE_RECEIVED);
        intent.putExtra("message", new cmdBean(request));//����ֻ�ǽ���һ�¼򵥵�ת����
        System.out.println(request.getHeader().toString());
        context.sendBroadcast(intent); // */

        Header header = request.getHeader();
        IMHandler handler = IMHandlerManager.getInstance().find(header.getHandlerId());
        if (handler != null) {
            ((AndroidHandler) handler).setContext(context);// context
            handler.dispatch(mConnection, request);
        } else {
            logger.warn("Not found handlerId: " + header.getHandlerId());
        }
    }

    public synchronized static ConnectorManager getManager(Context context) {
        if (manager == null) {
            manager = new ConnectorManager(context);
        }
        return manager;
    }

    private synchronized void syncConnection(final String ServerHost, final int ServerPort) {
        try {

            if (isConnected()) {
                return;
            }
            ChannelFuture channelFuture = bootstrap.connect(new InetSocketAddress(ServerHost, ServerPort)).sync(); //�����IP�Ͷ˿ڣ������Լ�����޸�
            channel = channelFuture.channel();
            if (channel != null) {
                System.out.println("#####################in syncConnection,channel is not null");
            } else
                System.out.println("#####################in syncConnection,channel is  null");
        } catch (Exception e) {

            Intent intent = new Intent();
            intent.setAction(ACTION_CONNECTION_FAILED);
            intent.putExtra("exception", e);
            context.sendBroadcast(intent);

            System.out.println("******************connnect server failed!  " + ServerHost + ":" + ServerPort);

        }

    }

    public void connect(final String ServerHost, final int ServerPort) {


        if (!netWorkAvailable(context)) {

            Intent intent = new Intent();
            intent.setAction(ACTION_CONNECTION_FAILED);
            intent.putExtra("exception", new NetWorkDisableException());
            context.sendBroadcast(intent);

            return;
        }

        Future<?> future = executor.submit(new Runnable() {
            @Override
            public void run() {
                syncConnection(ServerHost, ServerPort);
            }
        });
        try {
            if (future.get() != null) {
                connect(ServerHost, ServerPort);
            }
        } catch (Exception e) {

            connect(ServerHost, ServerPort);
            e.printStackTrace();
        }
    }

    public void send(final IntentBean intentBean) {
        final IMResponse response = intentBean.toResponse();
        executor.execute(new Runnable() {
            @Override
            public void run() {

                if (channel == null) {
                    System.out.println("channel is null");
                }
                if (channel != null && channel.isActive()) {
                    boolean isDone = channel.writeAndFlush(response).awaitUninterruptibly(5000);
                    if (isDone) {
                        Intent intent = new Intent();
                        intent.setAction(ACTION_SENT_SUCCESS);
                        //intent.putExtra("IMResponse", new cmdBean(response));
                        context.sendBroadcast(intent);
                    } else {
                        System.out.println("Just send failed,so just ,try again###########");

                        Intent intent = new Intent();
                        intent.setAction(ACTION_SENT_FAILED);
                        //intent.putExtra("IMResponse", new cmdBean(response));
                        intent.putExtra("exception", new WriteToClosedSessionException());
                        context.sendBroadcast(intent);
                    }
                } else {
                    System.out.println("channel invalid,try again###########");

                    Intent intent = new Intent();
                    intent.setAction(ACTION_SENT_FAILED);
                    //intent.putExtra("IMResponse",new cmdBean(response));

                    intent.putExtra("exception", new SessionDisableException());
                    context.sendBroadcast(intent);
                }
            }
        });
    }

    public void destroy() {
        if (manager.channel != null) {
            manager.channel.close();
        }
        loopGroup.shutdownGracefully();
        manager = null;
    }

    public boolean isConnected() {
        if (channel == null) {
            return false;
        }
        return channel.isActive();
    }

    public void closeSession() {
        if (channel != null) {
            channel.close();
        }
    }

    public void deliverIsConnected() {
        Intent intent = new Intent();
        intent.setAction(ACTION_CONNECTION_STATUS);
        intent.putExtra(PushManager.KEY_CONNECTION_STATUS, isConnected());
        context.sendBroadcast(intent);
    }

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {

        System.out.println("******************channelActive :" + ctx.channel().localAddress());

        Intent intent = new Intent();
        intent.setAction(ACTION_CONNECTION_SUCCESS);
        context.sendBroadcast(intent);
    }

    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {

        System.out.println("******************channelInactive:" + ctx.channel().localAddress());
        if (channel.id().asLongText().equals(ctx.channel().id().asLongText())) {
            Intent intent = new Intent();
            intent.setAction(ACTION_CONNECTION_CLOSED);
            context.sendBroadcast(intent);
        }
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {

        Intent intent = new Intent();
        intent.setAction(ACTION_UNCAUGHT_EXCEPTION);
        intent.putExtra("exception", cause.getCause());

        //System.out.println(cause.toString());
        cause.printStackTrace();
        context.sendBroadcast(intent);
    }


    public static boolean netWorkAvailable(Context context) {//
        try {
            ConnectivityManager nw = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
            NetworkInfo networkInfo = nw.getActiveNetworkInfo();
            return networkInfo != null;
        } catch (Exception e) {
        }

        return false;
    }
}
